package com.controller;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.ListDataModel;

import org.apache.log4j.Logger;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.springframework.web.jsf.FacesContextUtils;

import com.builder.ProductoBuilder;
import com.builder.ProveedorBuilder;
import com.builder.TarifarioBuilder;
import com.dao.BaseModelDAO;
import com.model.Producto;
import com.model.Proveedor;
import com.model.Tarifario;
import com.query.TarifarioQuery;
import com.type.TipoTarifarioEnum;
import com.util.JSFUtil;
import com.util.ValidatorUtil;
import com.view.ProductoPrecioView;
import com.view.ProductoView;
import com.view.ProveedorView;
import com.view.TarifarioView;

@ManagedBean
@ViewScoped
@SuppressWarnings("serial")
public class TarifarioController extends PaginableController<Tarifario> {

	private Logger log = Logger.getLogger(TarifarioController.class);
	private BaseModelDAO<Tarifario> dao;
	private BaseModelDAO<Proveedor> proveedorDao;
	private BaseModelDAO<Producto> productoDao;
	private Tarifario model;
	private TarifarioQuery query;
	private List<ProductoView> listaProducto;
	private ListDataModel<ProductoPrecioView> productoSeleccionadoDM;
	private List<ProductoPrecioView> listaProductoPrecio;
	private List<ProductoPrecioView> listaProductoPrecioFiltrado;
	private List<ProductoView> listaProductoSeleccionado;
	private List<ProveedorView> listaProveedor;
	private List<TipoTarifarioEnum> listaTipoTarifario;

	@ManagedProperty("#{tarifarioView}")
	private TarifarioView view;

	@ManagedProperty("#{productoPrecioView}")
	private ProductoPrecioView productoPrecioView;

	@ManagedProperty("#{tarifarioBuilder}")
	private TarifarioBuilder builder;

	@ManagedProperty("#{proveedorBuilder}")
	private ProveedorBuilder proveedorBuilder;

	@ManagedProperty("#{productoBuilder}")
	private ProductoBuilder productoBuilder;

	@SuppressWarnings("unchecked")
	public TarifarioController() {

		try {
			dao = (BaseModelDAO<Tarifario>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("tarifarioDAO");
			proveedorDao = (BaseModelDAO<Proveedor>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("proveedorDAO");
			productoDao = (BaseModelDAO<Producto>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("productoDAO");
			query = new TarifarioQuery();
			listaProductoPrecio = new ArrayList<ProductoPrecioView>();
			listaProductoSeleccionado = new ArrayList<ProductoView>();
			listaTipoTarifario = Arrays.asList(TipoTarifarioEnum.values());
			addEdit = false;

		} catch (Throwable e) {
			log.error("Error al inicializar la clase ProveedorController", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@PostConstruct
	public void init() {
		listaProducto = productoBuilder.toView(productoDao.getList());
		listaProveedor = proveedorBuilder.toView(proveedorDao.getList());
	}

	public TarifarioQuery getQuery() {
		return query;
	}

	public void setQuery(TarifarioQuery query) {
		this.query = query;
	}

	public Tarifario getModel() {
		return model;
	}

	public void setModel(Tarifario model) {
		this.model = model;
	}

	public TarifarioView getView() {
		return view;
	}

	public void setView(TarifarioView view) {
		this.view = view;
	}

	public List<ProductoView> getListaProducto() {
		return listaProducto;
	}

	public void setListaProducto(List<ProductoView> listaProducto) {
		this.listaProducto = listaProducto;
	}

	public List<ProductoPrecioView> getListaProductoPrecio() {
		return listaProductoPrecio;
	}

	public void setListaProductoPrecio(
			List<ProductoPrecioView> listaProductoPrecio) {
		this.listaProductoPrecio = listaProductoPrecio;
	}

	public List<ProductoPrecioView> getListaProductoPrecioFiltrado() {
		return listaProductoPrecioFiltrado;
	}

	public void setListaProductoPrecioFiltrado(
			List<ProductoPrecioView> listaProductoPrecioFiltrado) {
		this.listaProductoPrecioFiltrado = listaProductoPrecioFiltrado;
	}

	public List<ProductoView> getListaProductoSeleccionado() {
		return listaProductoSeleccionado;
	}

	public void setListaProductoSeleccionado(
			List<ProductoView> listaProductoSeleccionado) {
		this.listaProductoSeleccionado = listaProductoSeleccionado;
	}

	public List<ProveedorView> getListaProveedor() {
		return listaProveedor;
	}

	public void setListaProveedor(List<ProveedorView> listaProveedor) {
		this.listaProveedor = listaProveedor;
	}

	public ProveedorBuilder getProveedorBuilder() {
		return proveedorBuilder;
	}

	public void setProveedorBuilder(ProveedorBuilder proveedorBuilder) {
		this.proveedorBuilder = proveedorBuilder;
	}

	public ProductoBuilder getProductoBuilder() {
		return productoBuilder;
	}

	public void setProductoBuilder(ProductoBuilder productoBuilder) {
		this.productoBuilder = productoBuilder;
	}

	public ListDataModel<ProductoPrecioView> getProductoSeleccionadoDM() {
		return productoSeleccionadoDM;
	}

	public ProductoPrecioView getProductoPrecioView() {
		return productoPrecioView;
	}

	public void setProductoPrecioView(ProductoPrecioView productoPrecioView) {
		this.productoPrecioView = productoPrecioView;
	}

	public List<TipoTarifarioEnum> getListaTipoTarifario() {
		return listaTipoTarifario;
	}

	public TarifarioBuilder getBuilder() {
		return builder;
	}

	public void setBuilder(TarifarioBuilder builder) {
		this.builder = builder;
	}

	@Override
	public void query(ActionEvent event) {
		loadList();
	}

	@Override
	public void edit(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			model = dao.findFULL(model.getID());
			view = builder.toView(model);

			listaProductoPrecio = view.getListaProductoPrecio();
			productoSeleccionadoDM = new ListDataModel<ProductoPrecioView>(
					listaProductoPrecio);
			addEdit = true;
		} catch (Throwable e) {
			log.error("Error al editar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}

	}

	@Override
	public void delete(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			dao.delete(model);
			loadList();
		} catch (Throwable e) {
			log.error("Error al eliminar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void add(ActionEvent event) {
		clear();
		addEdit = true;
		clear();
	}

	@Override
	public void save(ActionEvent event) {

		try {

			view.setListaProductoPrecio(listaProductoPrecio);
			List<String> errores = new ValidatorUtil().validate(view);

			if (errores.size() == 0) {

				model = builder.toDomain(view);

				if (model.getID() != null) {
					dao.edit(model);
					addEdit = false;
				} else {
					dao.save(model);
				}
				clear();
				JSFUtil.saveMessage("Elemento guardado con exito",
						FacesMessage.SEVERITY_INFO);
				if (!addEdit) {
					loadList();
				}
			} else {
				JSFUtil.saveMessages(errores);
			}
		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void cancel(ActionEvent event) {
		addEdit = false;
		lazyDM = null;
		clear();
	}

	@Override
	public void clear() {
		view = new TarifarioView();
		model = new Tarifario();
		listaProductoPrecio = new ArrayList<ProductoPrecioView>();
		productoSeleccionadoDM = new ListDataModel<ProductoPrecioView>(
				listaProductoPrecio);
	}

	private void loadList() {

		lazyDM = new LazyDataModel<Tarifario>() {
			@Override
			public List<Tarifario> load(int first, int pageSize,
					String sortField, SortOrder sortOrder,
					Map<String, Object> filters) {

				Map<String, Object> filtro = new HashMap<String, Object>();
				filtro.put("fechaDesde", query.getFechaDesde());
				filtro.put("fechaHasta", query.getFechaHasta());
				filtro.put("tipoTarifario", query.getTipoTarifario());
				filtro.put("proveedor.id", query.getProveedorID());

				return dao.getList(first, pageSize, "id", true, filtro, true);
			}
		};

		Map<String, Object> filtro = new HashMap<String, Object>();
		filtro.put("fechaDesde", query.getFechaDesde());
		filtro.put("fechaHasta", query.getFechaHasta());
		filtro.put("tipoTarifario", query.getTipoTarifario());
		filtro.put("proveedor.id", query.getProveedorID());
		lazyDM.setRowCount(dao.count(filtro, true).intValue());
	}

	public void addProducto(ActionEvent event) {

		int contador = 0;

		// ProductoView productoView = productoDM.getRowData();
		for (ProductoView productoView : listaProductoSeleccionado) {
			ProductoPrecioView productoPrecio = new ProductoPrecioView(null,
					productoView, new BigDecimal(0));
			// veo si existe
			if (!listaProductoPrecio.contains(productoPrecio)) {
				listaProductoPrecio.add(productoPrecio);
				productoSeleccionadoDM = new ListDataModel<ProductoPrecioView>(
						listaProductoPrecio);
				contador++;
			}
		}

		listaProductoSeleccionado = new ArrayList<ProductoView>();

		JSFUtil.saveMessage("Se agregaron " + contador
				+ " productos a los seleccionados.", FacesMessage.SEVERITY_INFO);
	}

	public void deleteProducto(ActionEvent event) {

		ProductoPrecioView productoPrecioView = productoSeleccionadoDM
				.getRowData();

		listaProductoPrecio.remove(productoPrecioView);
		productoSeleccionadoDM = new ListDataModel<>(listaProductoPrecio);

	}

	public void tipoTarifarioListener() {
		if (isProveedor()) {
			view.setProveedor(new ProveedorView());
		} else {
			view.setProveedor(null);
		}
	}

	public boolean isProveedor() {
		return TipoTarifarioEnum.PROVEEDOR.equals(view.getTipoTarifario());
	}

	public void copy(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			model = dao.findFULL(model.getID());
			view = builder.toView(model);

			// es nuevo
			view.setId(null);

			listaProductoPrecio = view.getListaProductoPrecio();
			productoSeleccionadoDM = new ListDataModel<ProductoPrecioView>(
					listaProductoPrecio);
			addEdit = true;
		} catch (Throwable e) {
			log.error("Error al copiar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}

	}

}
